package org.javaboy.quartz_demo.service;

import org.javaboy.quartz_demo.job.ConcurrentJob;
import org.javaboy.quartz_demo.job.DisAllowConcurrentJob;
import org.javaboy.quartz_demo.job.JobConstants;
import org.javaboy.quartz_demo.model.QuartzBean;
import org.javaboy.quartz_demo.model.RespBean;
import org.quartz.*;
import org.quartz.impl.matchers.GroupMatcher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.QuartzJobBean;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

@Service
public class QuartzJobService {
    /**
     * Spring Boot 中的 starter 自动完成了 scheduler 的注入
     */
    @Autowired
    Scheduler scheduler;


    public RespBean addJob(QuartzBean quartzBean) {
        //先获取定时任务的类
        Class<? extends QuartzJobBean> jobClazz = quartzBean.getConcurrent() == 0 ? DisAllowConcurrentJob.class : ConcurrentJob.class;
        JobDataMap jobDataMap = new JobDataMap();
        jobDataMap.put(JobConstants.JOB_EXPRESSION, quartzBean.getJobExpression());
        //1. 创建 JobDetail
        JobDetail jobDetail = JobBuilder.newJob(jobClazz)
                //给这个 job 取个名字，这个名字在 scheduler 中是唯一的
                .withIdentity(quartzBean.getJobName(), quartzBean.getJobGroup())
                //关于任务的描述信息
                .withDescription(quartzBean.getDescription())
                //关于任务的参数信息
                .setJobData(jobDataMap)
                .build();
        //2. 创建 Trigger
        CronTrigger cronTrigger = createNewTrigger(quartzBean);
        //3. 注册 JobDetail 和 Trigger
        try {
            scheduler.scheduleJob(jobDetail, cronTrigger);
            return RespBean.ok("添加定时任务成功");
        } catch (SchedulerException e) {
//            throw new RuntimeException(e);
            return RespBean.error("添加定时任务失败", e.getMessage());
        }
    }

    private static CronTrigger createNewTrigger(QuartzBean quartzBean) {
        TriggerBuilder<CronTrigger> cronTriggerBuilder = TriggerBuilder.newTrigger()
                //给这个 trigger 取个名字，这个名字在 scheduler 中是唯一的
                .withIdentity(quartzBean.getJobName(), quartzBean.getJobGroup())
                .withSchedule(CronScheduleBuilder.cronSchedule(quartzBean.getCronExpression()));
        if (quartzBean.getStartTime() != null) {
            //开始时间，如果不设置，默认立即执行
            cronTriggerBuilder.startAt(quartzBean.getStartTime());
        }
        if (quartzBean.getEndTime() != null) {
            //结束时间，如果不设置，默认一直执行
            cronTriggerBuilder.endAt(quartzBean.getEndTime());
        }
        CronTrigger cronTrigger = cronTriggerBuilder.build();
        return cronTrigger;
    }

    public RespBean changeStatus(QuartzBean quartzBean) {
        //1. 根据 jobName 和 jobGroup 获取 JobDetail
        JobKey jobKey = JobKey.jobKey(quartzBean.getJobName(), quartzBean.getJobGroup());
        if (JobConstants.JOB_PAUSED.equals(quartzBean.getStatus())) {
            try {
                scheduler.pauseJob(jobKey);
                return RespBean.ok("暂停定时任务成功");
            } catch (SchedulerException e) {
//                throw new RuntimeException(e);
                return RespBean.error("暂停定时任务失败", e.getMessage());
            }
        } else if (JobConstants.JOB_RUNNING.equals(quartzBean.getStatus())) {
            try {
                scheduler.resumeJob(jobKey);
                return RespBean.ok("恢复定时任务成功");
            } catch (SchedulerException e) {
//                throw new RuntimeException(e);
                return RespBean.error("恢复定时任务失败", e.getMessage());
            }
        }
        return null;
    }

    public RespBean updateTrigger(QuartzBean quartzBean) {
        //1. 找到旧的 触发器
        TriggerKey oldTriggerKey = TriggerKey.triggerKey(quartzBean.getJobName(), quartzBean.getJobGroup());
        //如果只是想要更新触发器（更新 cron 表达式），可以使用下面这个方案
        //如果不仅是想要更新触发器，还想要更新 JobDetail，那么需要先删除原来的 JobDetail 和 Trigger，然后再重新添加
        try {
            scheduler.rescheduleJob(oldTriggerKey, createNewTrigger(quartzBean));
            return RespBean.ok("更新定时任务成功");
        } catch (SchedulerException e) {
//            throw new RuntimeException(e);
            return RespBean.error("更新定时任务失败", e.getMessage());
        }
    }

    public RespBean deleteJob(String jobName, String jobGroup) {
        try {
            scheduler.deleteJob(JobKey.jobKey(jobName, jobGroup));
            return RespBean.ok("删除定时任务成功");
        } catch (SchedulerException e) {
//            throw new RuntimeException(e);
            return RespBean.error("删除定时任务失败", e.getMessage());
        }
    }

    public RespBean updateJob(QuartzBean quartzBean) {
        //1. 删除旧的 job
        deleteJob(quartzBean.getJobName(), quartzBean.getJobGroup());
        //2. 添加新的 job
        return addJob(quartzBean);
    }

    public RespBean runOnce(String jobName, String jobGroup) {
        try {
            scheduler.triggerJob(JobKey.jobKey(jobName, jobGroup));
            return RespBean.ok("执行定时任务成功");
        } catch (SchedulerException e) {
//            throw new RuntimeException(e);
            return RespBean.error("执行定时任务失败", e.getMessage());
        }
    }

    public List<QuartzBean> getAllJobs() {
        List<QuartzBean> quartzBeans = new ArrayList<>();
        try {
            //1. 获取所有的任务组的名称
            List<String> jobGroups = scheduler.getJobGroupNames();
            //2. 遍历任务组，获取任务组中具体的任务
            for (String jobGroup : jobGroups) {
                Set<JobKey> jobKeys = scheduler.getJobKeys(GroupMatcher.jobGroupEquals(jobGroup));
                for (JobKey jobKey : jobKeys) {
                    //3. 获取任务详情
                    JobDetail jobDetail = scheduler.getJobDetail(jobKey);
                    //4. 获取触发器
                    //由于同一个 Job 中可能存在多个 触发器，所以这里需要遍历
                    List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
                    for (Trigger trigger : triggers) {
                        //5. 获取触发器对应的 cron 表达式
                        CronTrigger cronTrigger = (CronTrigger) trigger;
                        String cronExpression = cronTrigger.getCronExpression();
                        QuartzBean quartzBean = new QuartzBean();
                        quartzBean.setJobName(jobDetail.getKey().getName());
                        quartzBean.setJobGroup(jobDetail.getKey().getGroup());
                        quartzBean.setDescription(jobDetail.getDescription());
                        quartzBean.setJobExpression(jobDetail.getJobDataMap().getString(JobConstants.JOB_EXPRESSION));
                        quartzBean.setStartTime(
                                trigger.getStartTime() == null ? null : trigger.getStartTime());
                        quartzBean.setEndTime(
                                trigger.getEndTime() == null ? null : trigger.getEndTime());
                        quartzBean.setCronExpression(cronExpression);
                        //6. 获取任务的状态
                        Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
                        quartzBean.setStatus(triggerState.name());
                        quartzBeans.add(quartzBean);
                    }
                }
            }
        } catch (SchedulerException e) {
            throw new RuntimeException(e);
        }
        return quartzBeans;
    }
}
